#ifndef _tenacitas__exception__exception_h_
#define _tenacitas__exception__exception_h_

// =============================================================================
/** @file */

// =============================================================================
/** todo */

// =============================================================================
//                                 C++ Headers

#include <sstream>
#include <string>
#include <stdexcept>
#include <iostream>

// =============================================================================
//                                 3rds Headers


// =============================================================================
//                                 Our Headers


// =============================================================================
//                                 Namespaces
using namespace std;


// =============================================================================
//                                 Macro Contants
/** */


// =============================================================================
//                                 Macro Commands

/** macro commando to make it easier to create a log exception */
#define make_tenacitas_exception(msg) ( tenacitas::exception::exception (__FILE__, __func__, __LINE__,msg) )

/** namespace of the organization */
namespace tenacitas {
    /** namespace for the project */
    namespace exception {

        // =========================== Typedefs ===========================
        /** */

        // ============================ Pre-Declarations ==================

        // ============================ Attributes ========================
        /** */

        // ============================ Functions =========================
        /** */

        // ============================ Classes ===========================
        /** 
         Creates a exception associated to the tenacitas::log namespace

          <b>A word to the programmer user</b>
          You, dear programmer, should avoid creating the tenacitas::log::exception
          directly. Please, use the @p make_exception  defined above
            @nosubgrouping 
        */
        class exception  : public std::exception {

        public:

            // --- exception  - Public Constructors --------------------------

            /** Constructor

              @param [in] p_file is the name of the file from where the
              exception was thrown
              @param [in] p_function is the name of the function from where the
              exception was thrown
              @param [in] p_line is the line number from where the exception
              was thrown
              @param [in] p_msg is the cause for the exception
            */
            exception ( const std::string & p_file,
                       const std::string & p_function,
                       uint64_t p_line,
                       const std::string & p_msg );

            /**
             * @brief copy constructor
             */
            exception (const exception  & p_ex)
                : m_stream() {
                std::string l_aux = p_ex.m_stream.str();
                m_stream << l_aux;
                m_aux = m_stream.str( );                
            }

            exception (exception && p_ex)
                : m_stream() {
                std::string l_aux = p_ex.m_stream.str();
                m_stream << l_aux;
                m_aux = m_stream.str( );                
            }

            /** Destructor */
            ~exception ( ) throw() {}

            // --- exception  - Public Accessors -----------------------------

            /**
             * @brief what
             * @return the cause for the exception
             */
            const char * what() const noexcept {

                static const char * l_what = m_aux.c_str();
//                std::cout << "EXCEPTION: '" << l_what << "'"  << std::endl;
                return l_what;
            }

            // --- exception  - Public Operators -----------------------------

            /** Assignment operator */
            exception  & operator =(const exception  & p_ex) {
                if (this != & p_ex) {
                    std::string l_aux = p_ex.m_stream.str();
                    m_stream.str("");
                    m_stream << l_aux;
                    m_aux = m_stream.str( );                
                }
                return *this;
            }

            /** Assignment operator */
            exception  & operator =(exception && p_ex) {
                if (this != & p_ex) {
                    std::string l_aux = p_ex.m_stream.str();
                    m_stream.str("");
                    m_stream << l_aux;
                    m_aux = m_stream.str( );                
                }
                return *this;
            }

        private:

            // --- exception  - Private Attributes ---------------------------

            /** to format the exception message */
            std::stringstream m_stream;

            std::string m_aux;
        };
    }
}

// =============================================================================
//                     I M P L E M E N T A T I O N
// =============================================================================

using namespace tenacitas::exception;


#endif
